/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
* bug "inline method - doesn't handle implicit cast" (see
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
*******************************************************************************/
package org.eclipse.jdt.internal.corext.dom;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.PrimitiveType.Code;
/**
* Helper class to check if objects are assignable to each other.
*/
public class TypeRules {
/**
* Tests if a two primitive types are assign compatible
* @param toAssignCode The binding of the type to assign
* @param definedTypeCode The type of the object that is assigned
* @return boolean Returns true if definedType = typeToAssign is true
*/
public static boolean canAssignPrimitive(PrimitiveType.Code toAssignCode, PrimitiveType.Code definedTypeCode) {
// definedTypeCode = typeCodeToAssign;
if (toAssignCode == definedTypeCode) {
return true;
}
if (definedTypeCode == PrimitiveType.BOOLEAN || toAssignCode == PrimitiveType.BOOLEAN) {
return false;
}
if (definedTypeCode == PrimitiveType.CHAR && toAssignCode == PrimitiveType.BYTE) {
return false;
}
return getTypeOrder(definedTypeCode) > getTypeOrder(toAssignCode);
}
/**
* Tests if two types are assign compatible. Void types are never compatible.
* @param typeToAssign The binding of the type to assign
* @param definedType The type of the object that is assigned
* @return boolean Returns true if definedType = typeToAssign is true
*/
public static boolean canAssign(ITypeBinding typeToAssign, ITypeBinding definedType) {
//see bug 80715
// definedType = typeToAssign;
String voidName= PrimitiveType.VOID.toString();
if (voidName.equals(typeToAssign.getName()) || voidName.equals(definedType.getName())) {
return false;
}
if (typeToAssign.isNullType()) {
return !definedType.isPrimitive();
}
if (definedType.isArray()) {
if (!typeToAssign.isArray()) {
return false; // can not assign a non-array type to an array
}
int definedDim= definedType.getDimensions();
int toAssignDim= typeToAssign.getDimensions();
if (definedDim == toAssignDim) {
definedType= definedType.getElementType();
typeToAssign= typeToAssign.getElementType();
if (typeToAssign.isPrimitive() && typeToAssign != definedType) {
return false; // can't assign arrays of different primitive types to each other
}
// fall through
} else if (definedDim < toAssignDim) {
return isArrayCompatible(definedType.getElementType());
} else {
return false;
}
}
if (typeToAssign.isPrimitive()) {
if (!definedType.isPrimitive()) {
return false;
}
PrimitiveType.Code toAssignCode= PrimitiveType.toCode(typeToAssign.getName());
PrimitiveType.Code definedTypeCode= PrimitiveType.toCode(definedType.getName());
return canAssignPrimitive(toAssignCode, definedTypeCode);
} else {
if (definedType.isPrimitive()) {
return false;
}
if (typeToAssign.isArray()) {
return isArrayCompatible(definedType);
}
if (isJavaLangObject(definedType)) {
return true;
}
return Bindings.isSuperType(definedType, typeToAssign);
}
}
private static int getTypeOrder(Code type) {
if (type == PrimitiveType.BYTE)
return 2;
if (type == PrimitiveType.CHAR)
return 3;
if (type == PrimitiveType.SHORT)
return 3;
if (type == PrimitiveType.INT)
return 4;
if (type == PrimitiveType.LONG)
return 5;
if (type == PrimitiveType.FLOAT)
return 6;
if (type == PrimitiveType.DOUBLE)
return 7;
return 0;
}
public static boolean isArrayCompatible(ITypeBinding definedType) {
if (definedType.isTopLevel()) {
if (definedType.isClass()) {
return "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName()); //$NON-NLS-1$//$NON-NLS-2$
} else {
String qualifiedName= definedType.getQualifiedName();
return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return false;
}
public static boolean isJavaLangObject(ITypeBinding definedType) {
return definedType.isTopLevel() && definedType.isClass() && "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName()); //$NON-NLS-1$//$NON-NLS-2$
}
/**
* Tests if a two types are cast compatible
* @param castType The binding of the type to cast to
* @param bindingToCast The binding ef the expression to cast.
* @return boolean Returns true if (castType) bindingToCast is a valid cast expression (can be unnecessary, but not invalid).
*/
public static boolean canCast(ITypeBinding castType, ITypeBinding bindingToCast) {
//see bug 80715
String voidName= PrimitiveType.VOID.toString();
if (castType.isAnonymous() || castType.isNullType() || voidName.equals(castType.getName())) {
throw new IllegalArgumentException();
}
if (castType == bindingToCast) {
return true;
}
if (voidName.equals(bindingToCast.getName())) {
return false;
}
if (bindingToCast.isArray()) {
if (!castType.isArray()) {
return isArrayCompatible(castType); // can not cast an arraytype to a non array type (except to Object, Serializable...)
}
int toCastDim= bindingToCast.getDimensions();
int castTypeDim= castType.getDimensions();
if (toCastDim == castTypeDim) {
bindingToCast= bindingToCast.getElementType();
castType= castType.getElementType();
if (castType.isPrimitive() && castType != bindingToCast) {
return false; // can't assign arrays of different primitive types to each other
}
// fall through
} else if (toCastDim < castTypeDim) {
return isArrayCompatible(bindingToCast.getElementType());
} else {
return isArrayCompatible(castType.getElementType());
}
}
if (castType.isPrimitive()) {
if (!bindingToCast.isPrimitive()) {
return false;
}
String boolName= PrimitiveType.BOOLEAN.toString();
return (!boolName.equals(castType.getName()) && !boolName.equals(bindingToCast.getName()));
} else {
if (bindingToCast.isPrimitive()) {
return false;
}
if (castType.isArray()) {
return isArrayCompatible(bindingToCast);
}
if (castType.isInterface()) {
if ((bindingToCast.getModifiers() & Modifier.FINAL) != 0) {
return Bindings.isSuperType(castType, bindingToCast);
} else {
return true;
}
}
if (bindingToCast.isInterface()) {
if ((castType.getModifiers() & Modifier.FINAL) != 0) {
return Bindings.isSuperType(bindingToCast, castType);
} else {
return true;
}
}
if (isJavaLangObject(castType)) {
return true;
}
return Bindings.isSuperType(bindingToCast, castType) || Bindings.isSuperType(castType, bindingToCast);
}
}
}